﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using RemoteDesktopLib;
using System.Runtime.InteropServices;
using System.Collections;
using System.ServiceModel;

namespace RemoteDesktopClient
{
    public partial class Form1 : Form
    {

        private Viewer ViewerService;

        private int hLocalMouseHook;
        private RemoteDesktopLib.Win32Stuff.HookProc localMouseHookCallback;

        private int hLocalKeyboardHook;
        private RemoteDesktopLib.Win32Stuff.HookProc localKeyboardHookCallback;

        private ServerService.ServerClient serverService;

        private bool _bFocus;

        public Form1()
        {
            InitializeComponent();

            this.Activated += Form1_Activated;
            this.Deactivate += Form1_Deactivated;
        }

        public void ConfigServerService(string ip)
        {
            EndpointAddress endAddr = new EndpointAddress("http://" + ip + ":8733/Rlc/Server");
            BasicHttpBinding binding = new BasicHttpBinding();
            binding.TransferMode = TransferMode.Buffered;
            binding.MaxBufferPoolSize = long.MaxValue;
            binding.MaxBufferSize = int.MaxValue;
            binding.MaxReceivedMessageSize = int.MaxValue;
            binding.ReaderQuotas.MaxArrayLength = int.MaxValue;
            binding.ReaderQuotas.MaxBytesPerRead = int.MaxValue;
            binding.ReaderQuotas.MaxNameTableCharCount = int.MaxValue;
            binding.ReaderQuotas.MaxStringContentLength = int.MaxValue;
            serverService = new ServerService.ServerClient(binding, endAddr);
        }

        private bool SetWindowMouseHook()
        {
            localMouseHookCallback = new Win32Stuff.HookProc(this.MouseProc);

            hLocalMouseHook = Win32Stuff.SetWindowsHookEx(
                Win32Stuff.WH_MOUSE, 
                localMouseHookCallback, 
                IntPtr.Zero, 
                Win32Stuff.GetCurrentThreadId());

            return hLocalMouseHook != 0;
        }

        private bool SetWindowKeyBoardHook()
        {
            localKeyboardHookCallback = new Win32Stuff.HookProc(this.KeyboardProc);

            hLocalKeyboardHook = Win32Stuff.SetWindowsHookEx(
                Win32Stuff.WH_KEYBOARD, 
                localKeyboardHookCallback, 
                IntPtr.Zero, 
                Win32Stuff.GetCurrentThreadId());

            return hLocalKeyboardHook != 0;
        }

        private bool RemoveWindowMouseHook()
        {
            if(hLocalMouseHook != 0)
            {
                if (Win32Stuff.UnhookWindowsHookEx(hLocalMouseHook) == 0)
                    return false;
            }

            return true;
        }

        private bool RemoveWindowKeyBoardHook()
        {
            if (hLocalKeyboardHook != 0)
            {
                if (Win32Stuff.UnhookWindowsHookEx(hLocalKeyboardHook) == 0)
                    return false;
            }

            return true;
        }


        void Form1_Activated(object sender, EventArgs e)
        {
            _bFocus = true;
        }

        void Form1_Deactivated(object sender, EventArgs e)
        {
            _bFocus = false;
        }

        // Link https://msdn.microsoft.com/en-us/library/windows/desktop/ms644988%28v=vs.85%29.aspx
        public int MouseProc(int nCode, int wParam, IntPtr lParam)
        {
            if(_bFocus && nCode > -1 && pictureBox1.BackgroundImage != null)
            {
               // Xử lý các mouse event đc hook.
                CommandTypeOption commandType = CommandInfo.GetCommandType(wParam);
                
                if(commandType == CommandTypeOption.MouseMove)
                {
                    //Win32Stuff.MOUSEHOOKSTRUCT mouseHookStruct =
                    //    (Win32Stuff.MOUSEHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(Win32Stuff.MOUSEHOOKSTRUCT));

                    // Lấy vị trí của chuột trong pictureBox1.
                    Point point = pictureBox1.PointToClient(Cursor.Position);

                    // Chuyển sang tọa độ screen của server.
                    int cursorX = point.X * pictureBox1.BackgroundImage.Width / pictureBox1.Width;
                    int cursorY = point.Y * pictureBox1.BackgroundImage.Height / pictureBox1.Height;

                    string data = commandType.ToString() + "|" + cursorX.ToString() + "," + cursorY.ToString();
                    serverService.ProcessCommand(data); 

                }
                else if(commandType == CommandTypeOption.MouseWheel)
                {
                    Win32Stuff.MOUSEHOOKSTRUCTEX mouseHookStructEx =
                      (Win32Stuff.MOUSEHOOKSTRUCTEX)Marshal.PtrToStructure(lParam, typeof(Win32Stuff.MOUSEHOOKSTRUCTEX));

                    // Tính toán độ xoay của nút cuộn
                    short delta = (short)((mouseHookStructEx.mouseData >> 16) & 0xffff);

                    string data = commandType.ToString() + "|" + delta.ToString();
                    serverService.ProcessCommand(data);
                }
                else // MouseUp, MouseDown, MouseDoubleClick
                {
                    RemoteDesktopLib.MouseButtons button = CommandInfo.GetMouseButton(wParam);
                    string data = commandType.ToString() + "|" + button.ToString();
                    serverService.ProcessCommand(data);
                }
            }

            return Win32Stuff.CallNextHookEx(hLocalMouseHook, nCode, wParam, lParam);
        }


        // Link https://msdn.microsoft.com/en-us/library/windows/desktop/ms644984%28v=vs.85%29.aspx
        public int KeyboardProc(int nCode, int wParam, IntPtr lParam)
        {
            if (_bFocus && nCode == 0 && pictureBox1.BackgroundImage != null)
            {
                // Xử lý các keyboard event đc hook.

                // Lấy các thông tin cần thiết.
                Keys vkCode = (Keys)wParam;

                var i = lParam.ToInt64();
                byte[] bytes = BitConverter.GetBytes(i);
                var bits = new BitArray(bytes);
                CommandTypeOption commandType = bits[31] == true ? CommandTypeOption.KeyUp : CommandTypeOption.KeyDown;
                
                //Đóng gói dữ liệu theo cấu trúc.
                string data = commandType.ToString() + "|" + vkCode.ToString();
                // Gửi cho server.
                serverService.ProcessCommand(data);
            }

            return Win32Stuff.CallNextHookEx(hLocalKeyboardHook, nCode, wParam, lParam);
        }

        private void SvcOnImageChange(Image display)
        {
            if (display != null)
            {
                lock (display)
                {
                    this.Invoke(new MethodInvoker(delegate
                    {
                        pictureBox1.BackgroundImage = display;
                        pictureBox1.Refresh();
                    }));
                }
            }
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ViewerService = new Viewer();
            Viewer.OnImageChange += SvcOnImageChange;
            ViewerService.StartService();

            // Set Window Hook (Local Hook).
            if (!SetWindowMouseHook())
                MessageBox.Show(
                    "Set Window Mouse Hook Failed",
                    "Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);

            if (!SetWindowKeyBoardHook())
                MessageBox.Show(
                    "Set Window Keyboard Hook Failed",
                    "Error",
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error);
        }

        private void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.Parent = null;
            ViewerService.StopService();
            // Remove Window Hook (Local Hook).
            RemoveWindowMouseHook();
            RemoveWindowKeyBoardHook();
            
        }

        private void sendFileToolStripMenuItem_Click(object sender, EventArgs e)
        {
            upfileForm frmUpFile = new upfileForm();
            frmUpFile.serverClient = serverService;
            frmUpFile.Show();
        }
    }
}
